/*************************************
 * HEADER
 ************************************/

#include "dominion.h"
#include "dominion_helpers.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "coreUnitTestHelpers.h"
#include "coreUnitTests.h"
#include "rngs.h"

/*************************************
 ******** TESTS FOR getCost **********
 ************************************/
int getCostTester(){
  int result, i, card;
  long seed = floor(Random() * MODULUS);

  printf("\n***TESTING getCost()***\n"\
         "***********************\n");
  result = 0;
  PutSeed(seed);
  //test will be conducted 2000 times
  for(i = 0; i < 2000; i++){
  //generate a random number for a random card
    card = floor(Random() * NUM_CARDS);
    result += testCard(card);
  }      
  
  //print off the results of all tests
  printf ("\n***TESTS COMPLETE***\n***SUMMARY\n***Successful Tests: %d\n***Failed Tests: %d\n\n", 2000-result, result);
  
  return 0;
}

int testCard(int card){
  if(card == sea_hag || card == salvager || card == treasure_map || 
     card == cutpurse || card == baron || card == smithy || card == remodel || 
     card == gardens || card == feast){
    assert(getCost(card) == 4);
    return 0;
  }
  if(card == curse || card == copper){
    assert(getCost(card) == 0);
    return 0;
  }
  else if(card == embargo || card == estate){
    assert(getCost(card) == 2);
    return 0;
  }
  else if(card == silver || card == village || card == great_hall || 
	  card == steward || card == ambassador){
    assert(getCost(card) == 3);
    return 0;
  }
  if(card == outpost || card == tribute || card == minion || card == mine ||
     card == council_room || card == duchy){
    assert(getCost(card) == 5);
    return 0;
  }
  else if(card == gold || card == adventurer){
    assert(getCost(card) == 6);
    return 0;
  }
  else if(card == province){
    assert(getCost(card) == 8);
    return 0;
  }
  else{
    return 1;
  }
}


/*************************************
 ******** TESTS FOR scoreFor *********
 ************************************/
int scoreForTester(){
  int i, n, numPlayers, seed, success, failure, playerUnderTest, numToDiscard;
  int k[10] = {adventurer, council_room, feast, gardens, mine,
	       remodel, smithy, village, baron, great_hall};
  struct gameState G;
  
  printf ("\n***TESTING SCORE FOR***\n");

  PutSeed(floor(Random() * MODULUS));
  success = 0;
  failure = 0;

  for (n = 0; n < 2000; n++) {
    /* Fill the Game State with garbage */
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    //initialize new game
    numPlayers = floor(Random() * PLAYER_RANGE) + 2;
    seed = floor(Random() * 100000);
    initializeGame(numPlayers, k, seed, &G);

    playerUnderTest = floor(Random() * numPlayers); //set a player to be tested
    /* Draw a Five Card Hand */
    for(i = 0; i < 5; i++){
      drawCard(playerUnderTest, &G);
    }
    /* Move a random number of cards to the discard pile */
    numToDiscard = floor(Random() * G.deckCount[playerUnderTest]); // how many cards
    /* Move the cards, and adjust pile sizes */
    for(i = 0; i < numToDiscard; i++){
      G.discard[playerUnderTest][G.discardCount[playerUnderTest]] = G.deck[playerUnderTest][G.deckCount[playerUnderTest]];
      G.discardCount[playerUnderTest]++;
      G.deckCount[playerUnderTest]--;
    }
    
    G.whoseTurn = playerUnderTest; //set them to current player in gameState

    if(checkScore(playerUnderTest, &G) == 0){ //run test
      success++;
    }
    else{
      failure++;
    }
  }

  /* Because we have just initialized the game, each player should always have a 
     score of 3, since each game is initialized with each player having three 
     estate cards and no other point cards */
  printf ("\n***TESTS COMPLETE***\n***SUMMARY\n***Successful Tests: %d\n***Failed Tests: %d\n\n", success, failure);  
  return 0;
}


int checkScore(int player, struct gameState *testState){
  int i, testScore, ControlScore;
  struct gameState controlState;

  memcpy (&controlState, testState, sizeof(struct gameState));

  testScore = scoreFor(player, testState);

  /* SIMULATE THE FUNCTION EFFECT */
  ControlScore = 0;
  player = controlState.whoseTurn;
  for(i = 0; i < controlState.handCount[player]; i++){ //for each card in the player's hand
    if (controlState.hand[player][i] == curse) { ControlScore = ControlScore - 1; };
    if (controlState.hand[player][i] == estate) { ControlScore = ControlScore + 1; };
    if (controlState.hand[player][i] == duchy) { ControlScore = ControlScore + 3; };
    if (controlState.hand[player][i] == province) { ControlScore = ControlScore + 6; };
    if (controlState.hand[player][i] == great_hall) { ControlScore = ControlScore + 1; };
    if (controlState.hand[player][i] == gardens) { ControlScore = ControlScore + ( fullDeckCount(player, 0, &controlState) / 10 ); };
  }
  for(i = 0; i < controlState.discardCount[player]; i++){ //for each card in the player's discard pile
    if (controlState.discard[player][i] == curse) { ControlScore = ControlScore - 1; };
    if (controlState.discard[player][i] == estate) { ControlScore = ControlScore + 1; };
    if (controlState.discard[player][i] == duchy) { ControlScore = ControlScore + 3; };
    if (controlState.discard[player][i] == province) { ControlScore = ControlScore + 6; };
    if (controlState.discard[player][i] == great_hall) { ControlScore = ControlScore + 1; };
    if (controlState.discard[player][i] == gardens) { ControlScore = ControlScore + ( fullDeckCount(player, 0, &controlState) / 10 ); };
  }    
  for(i = 0; i < controlState.deckCount[player]; i++){ //for each card in the player's discard pile
    if (controlState.deck[player][i] == curse) { ControlScore = ControlScore - 1; };
    if (controlState.deck[player][i] == estate) { ControlScore = ControlScore + 1; };
    if (controlState.deck[player][i] == duchy) { ControlScore = ControlScore + 3; };
    if (controlState.deck[player][i] == province) { ControlScore = ControlScore + 6; };
    if (controlState.deck[player][i] == great_hall) { ControlScore = ControlScore + 1; };
    if (controlState.deck[player][i] == gardens) { ControlScore = ControlScore + ( fullDeckCount(player, 0, &controlState) / 10 ); };
  }    
  
  /* test to see if both the states are the same */
  if(ControlScore == testScore){
    return 0;
  }
  else{
    printf("***Failed Test***\nTest Score: %d\nExpected Score: %d\n\n", testScore, ControlScore);
    printf("Test Deck Count = %d\nTest Hand Count = %d\nTest Discard Count = %d\n", testState->deckCount[player], testState->handCount[player], testState->discardCount[player]);
    printf("Expected Deck Count = %d\nExpected Hand Count = %d\nExpected Discard Count = %d\n\n", controlState.deckCount[player], controlState.handCount[player], controlState.discardCount[player]);
    return 1;
  }
}



/*************************************
 ******** TESTS FOR endTurn **********
 ************************************/
int endTurnTester(){
  int i, j, n, numPlayers, seed, success, failure;
  int k[10] = {adventurer, council_room, feast, gardens, mine,
	       remodel, smithy, village, baron, great_hall};
  struct gameState G;
  
  printf ("\n***TESTING END TURN***\n");

  PutSeed(floor(Random() * MODULUS));
  success = 0;
  failure = 0;

  for (n = 0; n < 2000; n++) {
    /* Fill the Game State with garbage */
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    //initialize new game
    numPlayers = floor(Random() * PLAYER_RANGE) + 2;
    seed = floor(Random() * 1000);
    initializeGame(numPlayers, k, seed, &G);
    for (i = 0; i < numPlayers; i++){
      /* give each player a deck of random size */
      G.deckCount[i] = floor(Random() * MAX_DECK) + 5; //enough cards to draw from
      /* give each player a discard deck of random size */
      G.discardCount[i] = floor(Random() * MAX_DECK);
      /* give each player a hand of random size */
      G.handCount[i] = floor(Random() * MAX_HAND);
      /* fill each players' hand with random cards */
      for (j = 0; j < G.handCount[i]; j++){
	G.hand[i][j] = (Random() * (treasure_map + 1));
      }
    }

    if(checkEndTurn(&G) == 0){ //run test
      success++;
    }
    else{
      failure++;
    }
  }

  printf ("\n***TESTS COMPLETE***\n***SUMMARY\n***Successful Tests: %d\n***Failed Tests: %d\n\n", success, failure);  
  return 0;
}

int checkEndTurn(struct gameState *testState){
  int i, functionError, player;
  struct gameState controlState;

  memcpy (&controlState, testState, sizeof(struct gameState));

  functionError = endTurn(testState);

  /* SIMULATE THE FUNCTION EFFECT */
  player = controlState.whoseTurn;
  for(i = 0; i < controlState.handCount[player]; i++){
    controlState.discard[player][controlState.discardCount[player]++] = controlState.hand[player][i];//Discard
    controlState.hand[player][i] = -1;
  }
  controlState.handCount[player] = 0;

  if(player < (controlState.numPlayers - 1)){
    controlState.whoseTurn = player + 1; //increment player
  }
  else{
    controlState.whoseTurn = 0; //go back to first player
  }

  controlState.outpostPlayed = 0;
  controlState.phase = 0;
  controlState.numActions = 1;
  controlState.coins = 0;
  controlState.numBuys = 1;
  controlState.playedCardCount = 0;
  controlState.handCount[controlState.whoseTurn] = 0;

  //next player draws five cards
  for(i = 0; i < 5; i++){
    drawCard(controlState.whoseTurn, &controlState);
  }
  //update money
  updateCoins(controlState.whoseTurn, &controlState, 0);

  /* Assert that the function was called safely in the test state */
  assert(functionError == 0);

  /* test to see if both the states are the same */
  if(memcmp(&controlState, testState, sizeof(struct gameState)) == 0){
    return 0;
  }
  else{
    gameStateDiff(&controlState, testState);
    return 1;
  }
}


/*************************************
 ******** TESTS FOR isGameOver *******
 ************************************/
int isGameOverTester(){
  int i, n, numPlayers, seed, success, failure;
  int k[10] = {adventurer, council_room, feast, gardens, mine,
	       remodel, smithy, village, baron, great_hall};
  struct gameState G;
  
  printf ("\n***TESTING END GAME***\n");

  PutSeed(floor(Random() * MODULUS));
  success = 0;
  failure = 0;

  for (n = 0; n < 2000; n++) {
    /* Fill the Game State with garbage */
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    //initialize new game
    numPlayers = floor(Random() * PLAYER_RANGE) + 2;
    seed = floor(Random() * 100000);
    initializeGame(numPlayers, k, seed, &G);
    
    /* Randomly set stacks of things == 0 */
    for (i = 0; i < treasure_map + 1; i++){
      if((int)(floor(Random() * MODULUS)) % 10 == 0){ //should set pile to 0 appx 10% of the time
	G.supplyCount[i] = 0;
      }
    }

    if(checkEndGame(&G) == 0){ //run test
      success++;
    }
    else{
      failure++;
    }
  }

  printf ("\n***TESTS COMPLETE***\n***SUMMARY\n***Successful Tests: %d\n***Failed Tests: %d\n\n", success, failure);  
  return 0;
}

int checkEndGame(struct gameState *testState){
  int i, testEndGame, controlEndGame, controlCount;
  struct gameState controlState;

  memcpy (&controlState, testState, sizeof(struct gameState));

  testEndGame = 0;
  testEndGame = isGameOver(testState);

  /* SIMULATE THE FUNCTION EFFECT */
  controlEndGame = 0;
  controlCount = 0;

  if(controlState.supplyCount[province] == 0){
    controlEndGame = 1;
  }
  for (i = 0; i < treasure_map + 1; i++){
    if(controlState.supplyCount[i] == 0){
      controlCount++;
    }
  }
  if(controlCount >= 3){
    controlEndGame = 1;
  }
  
  /* test to see if both the states are the same */
  if(controlEndGame == testEndGame){
    return 0;
  }
  else{
    printf("***Failed Test***\nTest End Game: %d\nControl End Game: %d\n\n", testEndGame, controlEndGame);
    return 1;
  }
}


/*************************************
 ****** TESTS FOR numHandCards *******
 ************************************/
int numHandCardsTester(){
    int i; //loop counter
    int r; //initialization variable
    int failcount = 0; //track tests passed
    int passcount = 0; //track tests failed
    int check; //holds result returned from test function
    int seed = 1000;
    int numPlayer = 2;
    int k[10] = {adventurer, council_room, feast, gardens, mine
               , remodel, smithy, village, baron, great_hall};
    struct gameState G;

    printf("\nTesting numHandCards:\n\n");
    
    //test proper card return for both players
    for (i = 0; i < numPlayer; i++){

        memset(&G, 23, sizeof(struct gameState));   // clear the game state
        r = initializeGame(numPlayer, k, seed, &G); // initialize a new game

        G.handCount[0] = 0;
        G.handCount[1] = 10;
        G.whoseTurn = i;

        printf("Testing numHandCards() for player %d:\n", i);

        check = numHandCards(&G);
        printf("numHandCards returned %d,  ", check);
        printf("expected %d\n", 10 * i);
        if(check == 10 * i){
            printf("TEST PASSED\n\n");
            passcount++;
        }
        else{
            printf("TEST FAILED\n\n");
            failcount++;
        }
   }

    if (failcount == 0){
        printf("All tests passed!\n\n");
    }
    else{
        printf("%d tests failed. %d tests passed.\n", failcount, passcount);
        printf("Review log above for details.\n\n");
    }
    
    return 0;
}

